package joc.internal;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import joc.internal.Action.ActionType;
import joc.internal.Action.Command;
import joc.internal.compiler.StaticCall;
import joc.internal.util.SortedProperties;

import org.apache.log4j.Logger;

public class Evaluator {
	private static Logger logger = Logger.getLogger(Evaluator.class);

	public static final StaticCall evaluateContract = new StaticCall(Evaluator.class, "evaluateContract");

	public static void evaluateContract(EvaluationResult result) {
		if (!result.isSuccessful()) {
			String evaluationId = getEvaluationId(result);
			SortedProperties properties = ConfigCache.getConfig();
			Action action = new Action();
			for (String propertyKey : properties.stringPropertyNames(ConfigCache.PREFIX_ACTION)) {
				String propertyKeyForMatching = propertyKey.substring(ConfigCache.PREFIX_ACTION.length());
				propertyKeyForMatching = "\\Q" + propertyKeyForMatching + "\\E";
				propertyKeyForMatching = propertyKeyForMatching.replaceAll("\\*", Matcher.quoteReplacement("\\E.*\\Q"));
				propertyKeyForMatching = propertyKeyForMatching.replaceAll("\\?",
						Matcher.quoteReplacement("\\E[^\\.]*\\Q"));
				logger.info(propertyKeyForMatching);
				if (Pattern.matches(propertyKeyForMatching, evaluationId)) {
					String[] commands = properties.getProperty(propertyKey).split(",");
					for (String commandString : commands) {
						commandString = commandString.trim();
						try {
							action.addCommand(Command.valueOf(commandString));
						} catch (IllegalArgumentException e) {
							// ignore command
						}
					}
				}
			}
			AssertionError error = getAssertionError(result, evaluationId);
			if (action.hasAction(ActionType.LOG)) {
				logger.info("logging to " + getRecipientClass(result));
				Logger.getLogger(getRecipientClass(result)).error(error.getMessage(), error);
			}
			if (action.hasAction(ActionType.ASSERT)) {
				throw error;
			}
		}
	}

	protected static String getRecipientClass(EvaluationResult result) {
		String recipient = result.getRecipient();
		if (recipient.contains("%")) {
			return recipient.substring(0, recipient.indexOf('%'));
		}
		if (recipient.contains("(")) {
			return recipient.substring(0, recipient.indexOf('('));
		}
		return recipient;
	}

	protected static String getRecipientPackage(EvaluationResult result) {
		String recipientClass = getRecipientClass(result);
		return recipientClass.substring(0, recipientClass.lastIndexOf('.'));
	}

	protected static String getEvaluationId(EvaluationResult result) {
		return result.getRecipient() + "@" + result.getAnnotationClass().getName();
	}

	private static AssertionError getAssertionError(EvaluationResult result, String evaluationId) {
		AssertionError assertionError = new AssertionError("Contract violation for "
				+ result.getAnnotationClass().getName() + ": " + result.getErrorMessage() + " (ID: " + evaluationId
				+ ")");
		if (assertionError.getStackTrace() != null && assertionError.getStackTrace().length > 1) {
			LinkedList<StackTraceElement> stackTraceList = new LinkedList<StackTraceElement>(
					Arrays.asList(assertionError.getStackTrace()));
			stackTraceList.removeFirst();
			stackTraceList.removeFirst();
			assertionError.setStackTrace(stackTraceList.toArray(new StackTraceElement[] {}));
		}
		return assertionError;
	}
}
